package com.dm.oauth2.config;


import com.dm.commons.util.ConstantUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Collections;

/**
 * 授权服务的配置
 * */
@Configuration
@EnableAuthorizationServer    //授权服务器
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    @Resource
    private TokenStore tokenStore;
    @Resource
    private ClientDetailsService clientDetailsService;
    @Resource
    private AuthorizationCodeServices authorizationCodeServices;   //授权码模式需要
    @Resource
    private AuthenticationManager authenticationManager;  //认证管理器：密码模式需要
    @Resource
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Resource
    private PasswordEncoder passwordEncoder;

    /*将客户端信息存储到数据库*/
    @Bean
    public ClientDetailsService clientDetailsService(DataSource dataSource){
        ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
        ((JdbcClientDetailsService)clientDetailsService).setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    /**
     * 客户端详情配置
     * */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //采用数据库方式
        clients.withClientDetails(clientDetailsService);
    }

    /**
     * 令牌管理服务
     * */
    @Bean
    public AuthorizationServerTokenServices tokenServices(){
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore); //配置令牌存储策略
        tokenServices.setClientDetailsService(clientDetailsService);//由于是客户端颁发令牌，所以要配客户端详情服务
        tokenServices.setSupportRefreshToken(true); //支持刷新令牌
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();  //令牌JWT增强
        tokenEnhancerChain.setTokenEnhancers(Collections.singletonList(jwtAccessTokenConverter));
        tokenServices.setTokenEnhancer(tokenEnhancerChain);

        tokenServices.setAccessTokenValiditySeconds(ConstantUtil.EXPIRATION);  //令牌有效期2小时
        tokenServices.setRefreshTokenValiditySeconds(ConstantUtil.REFRESH_EXPIRATION); //刷新令牌有效期3天
        return tokenServices;
    }

    //设置授权码模式的授权码如何存取，采用数据库方式
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource){
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    /**
     * 令牌访问端点
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager)  //认证管理器
                .authorizationCodeServices(authorizationCodeServices) //授权码服务
                .tokenServices(tokenServices())  //令牌管理服务
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

    /**
     * 令牌端点的安全约束
     * */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")   //  /oauth/token_key是公开的，用户需要登录就可以拿公钥
                .checkTokenAccess("permitAll()")    //  /oauth/check_token是公开的  验证token的合法性
                .allowFormAuthenticationForClients();   //允许表单认证（申请令牌）
    }


    /**
     * 授权码模式：
     *      1、先获取授权码code(携带客户端id)
     *          http://localhost:53020/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com
     *      2、根据授权码code获取token令牌
     *          http://localhost:53020/oauth/token 这里是POST提交
     *              grant_type=authorization_code表示授权码模式
     *              client_id标识客户端id
     *              client_secret表示客户端密钥
     *              code表示第一步获取的授权码code
     *              redirect_uri返回的uri地址
     *
     * 简化模式：
     *      http://localhost:53020/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com
     *      直接返回token和一些其他信息
     *
     * 密码模式：
     *      http://localhost:53020/oauth/token 这里是POST提交
     *          grant_type=password表示密码模式
     *          client_id标识客户端id
     *          client_secret表示客户端密钥
     *          username用户名
     *          password密码
     *
     * 客户端模式：
     *      http://localhost:53020/oauth/token 这里是POST提交
     *          grant_type=client_credentials表示客户端模式
     *          client_id标识客户端id
     *          client_secret表示客户端密钥
     *
     * */
}
